﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;

namespace WXR2BlogML
{
	public static class WxrBlogMlExporter
	{
		public static XNamespace blogMLNs
		{
			get
			{
				if (BlogMlSettings.Blogmltype == BlogMlSettings.Blogml25)
					return "http://blogml.org/2008/01/BlogML";
				
				return "http://www.blogml.com/2006/09/BlogML";
			}
		}

		public static string TagCategoryDescriptionQualifier = "__tag__";
			
		public static XNamespace dc = "http://purl.org/dc/elements/1.1/";
		public static XNamespace wp = "http://wordpress.org/export/1.0/";
		public static XNamespace excerptNs = "http://wordpress.org/export/1.0/excerpt/";
		public static XNamespace contentNs = "http://purl.org/rss/1.0/modules/content/";

		public static CultureInfo ci = new CultureInfo("en-US");
		public static string dateFormat = "yyyy-MM-ddTHH:mm:ss";

		public static DateTime ParseWPDate(string value)
		{
			return DateTime.ParseExact(value, "ddd, dd MMM yyyy HH:mm:ss zz00", ci.DateTimeFormat);
		}

		public static DateTime ParseWPPostDate(string value)
		{
			return DateTime.ParseExact(value, "yyyy-MM-dd HH:mm:ss", ci.DateTimeFormat, DateTimeStyles.AssumeUniversal);
		}

		public static string FormatWpDate(DateTime date)
		{
			return date.ToString(dateFormat, new DateTimeFormatInfo() { TimeSeparator = ":" });
		}


		public static string ToRelativeUrl(string url)
		{
			Uri uri = new Uri(url);
			return uri.PathAndQuery;
		}

		public static XElement CreateAuthors(XElement channelWxr)
		{
			var authors = (from creator in channelWxr.Elements("item").Elements(dc + "creator")
								select ((XCData)creator.FirstNode).Value).Distinct()
				 .Select((name, i) => new { Name = name, Id = i + 1000 });
			var authorElements = from a in authors
										select new XElement(blogMLNs + "author",
																  new XAttribute("id", a.Id),
																  new XAttribute("date-created", FormatWpDate(DateTime.Now)),
																  new XAttribute("date-modified", FormatWpDate(DateTime.Now)),
																  new XAttribute("approved", "true"),
																  new XAttribute("email", a.Name.Replace(" ", "-") + "@blogml.org"),
																  new XElement(blogMLNs + "title",
																					new XAttribute("type", "text"),
																					new XCData(a.Name))
											 );

			return new XElement(blogMLNs + "authors", authorElements);
		}

		public static XElement CreateTags(XElement channelWxr)
		{
			var tags = (from tag in channelWxr.Elements(wp + "tag").Elements(wp + "tag_name")
							select ((XCData)tag.FirstNode).Value);
			var tagElements = from t in tags
									select new XElement(blogMLNs + "tag",
															  new XAttribute("description", t),
															  new XElement(blogMLNs + "title",
																				new XAttribute("type", "text"),
																				new XCData(t))
										 );

			return new XElement(blogMLNs + "tags", tagElements);
		}


		public static XElement CreateExtendedProperties()
		{
			return new XElement(blogMLNs + "extended-properties",
									  new XElement(blogMLNs + "property",
														new XAttribute("name", "CommentModeration"),
														new XAttribute("value", "Anonymous")),
									  new XElement(blogMLNs + "property",
														new XAttribute("name", "SendTrackback"),
														new XAttribute("value", "Yes"))

				 );
		}
		
		public static XElement CreateCategories(XElement channelWxr)
		{
			var cats = (from cat in channelWxr.Elements(wp + "category")
							where cat.Element(wp + "cat_name") != null
							select (new
							{
								Id = ((XCData)cat.Element(wp + "cat_name").FirstNode).Value,
								Name = ((XCData)cat.Element(wp + "cat_name").FirstNode).Value,
								ParentId = cat.Element(wp + "category_parent") != null ? cat.Element(wp + "category_parent").Value : ""
							}));
							
				 // .Select((name, i) => new { Name = name, Id = name });

			var categoryElements = from c in cats
										  select new XElement(blogMLNs + "category",
																  new XAttribute("id", c.Id),
																  new XAttribute("date-created", FormatWpDate(DateTime.Now)),
																  new XAttribute("date-modified", FormatWpDate(DateTime.Now)),
																  new XAttribute("approved", "true"),
																  new XAttribute("description", ""),
																  new XAttribute("parentref", c.ParentId),
																  new XElement(blogMLNs + "title",
																					new XAttribute("type", "text"),
																					new XCData(c.Name))
											 );
#if POSTTAGS
			// we handle the tags as categories with the description attribute set to '__tag__'
			// during the import we are then able to strip away these 'false' categories and replace them with post tags
			// this is a very ugly trick
			var tags = (from tag in channelWxr.Elements(wp + "tag").Elements(wp + "tag_slug")
							select ((XText)tag.FirstNode).Value)
				 .Select(name => new { Name = name, Id = name.SanitizeTagSlug() });
			var tagsElements = from t in tags
										  select new XElement(blogMLNs + "category",
																  new XAttribute("id", t.Id),
																  new XAttribute("date-created", FormatWpDate(DateTime.Now)),
																  new XAttribute("date-modified", FormatWpDate(DateTime.Now)),
																  new XAttribute("approved", "true"),
																  new XAttribute("description", TagCategoryDescriptionQualifier),
																  new XAttribute("parentref", "0"),
																  new XElement(blogMLNs + "title",
																					new XAttribute("type", "text"),
																					new XCData(t.Name))
											 );

         return new XElement(blogMLNs + "categories", categoryElements.Union(tagsElements));
#else
			return new XElement(blogMLNs + "categories", categoryElements);
#endif
		}

		public static XElement CreatePosts(XElement channelWxr, XElement allAuthors, XElement AllCats)
		{
			var posts = (from post in channelWxr.Elements("item")
							 where post.Element(wp + "post_type").Value == "post"
							 select new
							 {
								 Title = post.Element("title").Value,
								 PostName = post.Element(wp + "post_name").Value,
								 Date = ParseWPDate(post.Element("pubDate").Value),
								 AuthorName = ((XCData)post.Element(dc + "creator").FirstNode).Value,
								 CategorieNames = from c in post.Elements("category")
														where c.Attribute("domain") != null && c.Attribute("domain").Value == "category"
														select ((XCData)c.FirstNode).Value,
								 Tags = ExtractTags(post),
								 Content = WordpressExportFix.ConvertResourceAddresses(post.Element(contentNs + "encoded").Value, ((XCData)post.Element(dc + "creator").FirstNode).Value),
								 Exerpt = post.Element(excerptNs + "encoded").Value,
								 Id = post.Element(wp + "post_id").Value,
								 Link = post.Element("link").Value,
								 Description = post.Element("description").Value,
								 
								 Comments = from comment in post.Elements(wp + "comment")
												where comment.Element(wp + "comment_approved").Value == "1"
												select new
												{
													Id = comment.Element(wp + "comment_id").Value,
													AuthorName = comment.Element(wp + "comment_author").Value,
													AuthorEmail = comment.Element(wp + "comment_author_email").Value,
													AuthorIp = comment.Element(wp + "comment_author_IP").Value,
													AuthorUrl = comment.Element(wp + "comment_author_url").Value,

													Date = ParseWPPostDate(comment.Element(wp + "comment_date_gmt").Value),
													Content = ((XCData)comment.Element(wp + "comment_content").FirstNode).Value,
													Approved = comment.Element(wp + "comment_approved").Value == "1"
												}

							 });
			var postElements = from p in posts
									 select new XElement(blogMLNs + "post",
																new XAttribute("id", p.Id),
																new XAttribute("date-created", FormatWpDate(p.Date)),
																new XAttribute("date-modified", FormatWpDate(p.Date)),
																new XAttribute("approved", "true"),
																new XAttribute("post-url", ToRelativeUrl(p.Link)),
																new XAttribute("type", "normal"),
																new XAttribute("hasexcerpt", "false"),
																new XAttribute("views", "0"),
																new XElement(blogMLNs + "title",
																				 new XAttribute("type", "text"),
																				 new XCData(p.Title)),
																new XElement(blogMLNs + "content",
																				 new XAttribute("type", "text"),
																				 new XCData(p.Content)),
																new XElement(blogMLNs + "post-name",
																				 new XAttribute("type", "text"),
																				 new XCData(p.PostName)), //or title??
#if POSTTAGS
																new XElement(blogMLNs + "categories",
																				 (from c in p.CategorieNames
																				 select new XElement
																					  (blogMLNs + "category",
																						new XAttribute("ref",
																											AllCats.Elements(blogMLNs + "category").Where(cat => ((XCData)cat.Element(blogMLNs + "title")
																														 .FirstNode).Value == c).First().Attribute("id").Value)))
																					.Union(from t in p.Tags
																					select new XElement
																					  (blogMLNs + "category",
																						new XAttribute("ref", TagCategoryDescriptionQualifier + t.SanitizeTagSlug())))),
#else
																new XElement(blogMLNs + "categories",
																				 from c in p.CategorieNames
																				 select new XElement
																					  (blogMLNs + "category",
																						new XAttribute("ref",
																											AllCats.Elements(blogMLNs + "category").Where(cat => ((XCData)cat.Element(blogMLNs + "title")
																														 .FirstNode).Value == c).First().Attribute("id").Value))),
#endif
																new XElement(blogMLNs + "comments",
																				 from c in p.Comments
																				 select new XElement
																					  (blogMLNs + "comment",
																						new XAttribute("id", c.Id),
																						new XAttribute("date-created", FormatWpDate(c.Date)),
																						new XAttribute("date-modified", FormatWpDate(c.Date)),
																						new XAttribute("approved", c.Approved.ToString().ToLower()),
																						new XAttribute("user-name", XhtmlToXmlUtils.RemoveAll(c.AuthorName)),
																						new XAttribute("user-url", c.AuthorUrl),
																						new XElement(blogMLNs + "title",
																										 new XAttribute("type", "text"),
																										 new XCData(p.Title)),

																						new XElement(blogMLNs + "content",
																										 new XAttribute("type", "text"),
																										 new XCData(c.Content))
																					  )

																	 ),
																new XElement(blogMLNs + "authors",
																				 new XElement(blogMLNs + "author",
																						new XAttribute("ref",
																											allAuthors.Elements(blogMLNs + "author").Where(a => ((XCData)a.Element(blogMLNs + "title")
																														 .FirstNode).Value == p.AuthorName).First().Attribute("id").Value)))


										  );

			return new XElement(blogMLNs + "posts", postElements);
		}

		private static IEnumerable<string> ExtractTags(XElement post)
		{
			if (BlogMlSettings.Blogmltype == BlogMlSettings.Blogml25)
				return from c in post.Elements("category")
				       where
				       	c.Attribute("domain") != null && c.Attribute("domain").Value == "tag" && c.Attribute("nicename") == null
				       select ((XCData) c.FirstNode).Value;
#if POSTTAGS
			return from c in post.Elements("category")
			       where c.Attribute("domain") != null && c.Attribute("domain").Value == "tag" && c.Attribute("nicename") != null
			       select c.Attribute("nicename").Value;
#endif
		}
	}
}
